package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BooleanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.po.PointsRecord;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.mapper.PointsRecordMapper;
import com.tianji.learning.mq.message.SignInMessage;
import com.tianji.learning.service.ISignResultService;
import io.netty.handler.codec.mqtt.MqttFixedHeader;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

@RequiredArgsConstructor
@Service
public class SignResultServiceImpl extends ServiceImpl<PointsRecordMapper, PointsRecord> implements ISignResultService {

    private final StringRedisTemplate redisTemplate;
    private final RabbitMqHelper mqHelper;


    /**
     * 签到
     *
     * @return
     */
    @Override
    public SignResultVO addSign() {
        //1.获得用户id
        Long user = UserContext.getUser();

        //2.获得当前日期
        LocalDate now = LocalDate.now();

        //3.拼装key
   String key= RedisConstants.SIGN_RECORD_KEY_PREFIX
           +user
           +now.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);

        // 4.计算offset
        int offset = now.getDayOfMonth() - 1;

        //5.签到
        Boolean aBoolean = redisTemplate.opsForValue().setBit(key, offset, true);

        if (BooleanUtils.isTrue(aBoolean)){
            throw new BizIllegalException("重复签到");
        }

        //6.获取连续签到天数
        int i = countSignDays(key, now.getDayOfMonth());

        Integer rewardPoints=0;

        switch (i){
            case 7:
                rewardPoints=10;
                break;
            case 14:
                rewardPoints=20;
                break;
            case 24:
                rewardPoints=40;
                break;
        }

        SignResultVO signResultVO = new SignResultVO();
        signResultVO.setSignDays(i);
        signResultVO.setRewardPoints(rewardPoints);

        try {
            // 4.保存积分明细记录
            mqHelper.send(
                    MqConstants.Exchange.LEARNING_EXCHANGE,
                    MqConstants.Key.SIGN_IN,
                    SignInMessage.of(user, rewardPoints + 1));// 签到积分是基本得分+奖励积分
        } catch (Exception e) {
            log.error("签到发送新增积分消息失败：{}",e);
        }

        return signResultVO;
    }

    /**
     * 查询签到结果
     *
     * @return
     */
    @Override
    public Byte[] getSigns() {
        // 1、获取key
        // 1.1、获取当前登录用户
        Long userId = UserContext.getUser();

        // 1.2、获取当前日期
        LocalDate now = LocalDate.now();
        String dateStr = now.format(DateUtils.POINTS_BOARD_SUFFIX_FORMATTER);

        // 1.3、拼接key
        String key = RedisConstants.SIGN_RECORD_KEY_PREFIX + userId + dateStr;

        // 2、查询截止到当天的签到记录
        int dayOfMonth = now.getDayOfMonth();
        List<Long> result = redisTemplate.opsForValue()
                .bitField(key,
                        BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
                );
        if (ObjectUtil.isEmpty(result)) {
            return new Byte[]{};
        }
        int signResult = result.get(0).intValue();

        // 3、循环每一天的签到情况，赋值给byte数组
        Byte[] arr = new Byte[dayOfMonth];

        int pos = dayOfMonth - 1;
        while (pos >= 0) {
            arr[pos--] = (byte) (signResult & 1);
            signResult >>>= 1;
        }

        return arr;
    }

    private int countSignDays(String key, int len) {

        List<Long> longs = redisTemplate
                .opsForValue()
                .bitField(key,
                        BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(len)).valueAt(0));

        if (CollUtils.isEmpty(longs)){
            return 0;
        }
        int i = longs.get(0).intValue();

        int count = 0;
        while ((i&1)==1){
            count++;
            i>>>=1;
        }

        return count;
    }
}
